#include "MyRendererUtil.hpp"

void RendererUtil::DrawLine(Framebuffer &fb,
                            int x0, int y0,
                            int x1, int y1,
                            const Vector4<float> &color,
                            float depth)
{
    int dx = x1 - x0;
    int dy = y1 - y0;

    int stepX = 1, stepY = 1;

    if (dx < 0) { stepX = -1; dx = -dx; }
    if (dy < 0) { stepY = -1; dy = -dy; }

    int sx = x0;
    int sy = y0;

    if (dy <= dx)
    {
        int d2y = 2 * dy;
        int d2y_minus_d2x = d2y - 2 * dx;
        int flag = d2y - dx;

        for (int i = 0; i <= dx; ++i)
        {
            if (sx >= 0 && sx < fb.GetWidth() &&
                sy >= 0 && sy < fb.GetHeight())
            {
                fb.DrawPixel(sx, sy, color, depth);
            }

            sx += stepX;

            if (flag <= 0)
                flag += d2y;
            else
            {
                sy += stepY;
                flag += d2y_minus_d2x;
            }
        }
    }
    else
    {
        int d2x = 2 * dx;
        int d2x_minus_d2y = d2x - 2 * dy;
        int flag = d2x - dy;

        for (int i = 0; i <= dy; ++i)
        {
            if (sx >= 0 && sx < fb.GetWidth() &&
                sy >= 0 && sy < fb.GetHeight())
            {
                fb.DrawPixel(sx, sy, color, depth);
            }

            sy += stepY;

            if (flag <= 0)
                flag += d2x;
            else
            {
                sx += stepX;
                flag += d2x_minus_d2y;
            }
        }
    }
}

void RendererUtil::DrawTriangle(
    Framebuffer &fb,
    int x0, int y0,
    int x1, int y1,
    int x2, int y2,
    const Vector4<float> &color,
    float depth)
{
    struct V2 { int x, y; };
    V2 v[3] = { {x0, y0}, {x1, y1}, {x2, y2} };

    if (v[0].y > v[1].y) std::swap(v[0], v[1]);
    if (v[0].y > v[2].y) std::swap(v[0], v[2]);
    if (v[1].y > v[2].y) std::swap(v[1], v[2]);

    V2 v0 = v[0];
    V2 v1 = v[1];
    V2 v2 = v[2];

    auto edge_lerp = [&](V2 a, V2 b, int y)
    {
        if (a.y == b.y) return (float)a.x;  

        float t = (float)(y - a.y) / (float)(b.y - a.y);
        return a.x + t * (b.x - a.x);
    };

    auto draw_scanline = [&](int y, int xL, int xR)
    {
        if (xL > xR) std::swap(xL, xR);
        for (int x = xL; x <= xR; ++x)
            fb.DrawPixel(x, y, color, depth);
    };

    if (v1.y == v2.y)
    {
        for (int y = v0.y; y <= v2.y; ++y)
        {
            float xL = edge_lerp(v0, v1, y);
            float xR = edge_lerp(v0, v2, y);
            draw_scanline(y, (int)xL, (int)xR);
        }
        return;
    }

    if (v0.y == v1.y)
    {
        for (int y = v0.y; y <= v2.y; ++y)
        {
            float xL = edge_lerp(v0, v2, y);
            float xR = edge_lerp(v1, v2, y);
            draw_scanline(y, (int)xL, (int)xR);
        }
        return;
    }

    float t = (float)(v1.y - v0.y) / (float)(v2.y - v0.y);
    V2 v12;
    v12.y = v1.y;
    v12.x = v0.x + t * (v2.x - v0.x);

    for (int y = v0.y; y <= v1.y; ++y)
    {
        float xL = edge_lerp(v0, v1, y);
        float xR = edge_lerp(v0, v12, y);
        draw_scanline(y, (int)xL, (int)xR);
    }

    for (int y = v1.y; y <= v2.y; ++y)
    {
        float xL = edge_lerp(v1, v2, y);
        float xR = edge_lerp(v12, v2, y);
        draw_scanline(y, (int)xL, (int)xR);
    }
}
